package Twitim::Preferences;

use strict;
use warnings;
use utf8;

use Gtk2;
use Gtk2::SimpleList;
use Glib qw(TRUE FALSE);

use Twitim::L10N;
use Twitim::Twitim;

my $Mainwin;
my $Cfg;

binmode( STDOUT, ':utf8' );

###########################################################################
# preferences methods

sub new {
    my $this  = shift;
    my $class = ref($this) || $this;
    my $self  = {@_};
    bless $self, $class;
}

sub start {
    ( my $self, $Cfg, $Mainwin ) = @_;

    my $top = $self->{'preferences_win'}
        = _make_prefs_top_win( 'Twitim Preferences',
        $Mainwin->{'top'}, 350, 400 );

    my $mainbox = Gtk2::VBox->new( 0, 12 );
    $top->add($mainbox);

    my $notebook = $self->{'preferences_notebook'} = Gtk2::Notebook->new();
    $mainbox->pack_start( $notebook, 1, 1, 0 );

    # accounts
    my $account_box = _make_vbox( $notebook, "Accounts" );

    my $box2 = _make_frame( $account_box, "Twitter", 2, 2 );
    my ( $box1, $box1_label ) = _make_frame( $account_box, "XMPP", 2, 2, 1 );

    $self->{'entry'}->{'xmpp_user'}
        = text_entry( $box1, "User _Name", $Cfg->{'default'}->{'xmpp_user'} );
    $self->{'entry'}->{'xmpp_pass'}
        = text_entry( $box1, "Pass_word", $Cfg->{'default'}->{'xmpp_pass'},
        1 );

    my $label_text_for_user = $Cfg->{'default'}->{'access_secret'} ? 
        $Cfg->{'default'}->{'twitter_user'} : l10n("Unsetting");
    my $label_for_user = text_entry( $box2, "User Name", $label_text_for_user, 
                                     undef, undef, undef, Gtk2::Label->new );
    my $auth_button = 
        _make_auth_button( 
            $box2, 'gtk-new',
            sub { 
                my $assistante;
                eval { use Twitim::Assistant; };

                $assistante = Twitim::Assistant->new(
                    quit_when_cancel => FALSE,
                    on_destroy => sub {
                        my %access_token = $assistante->get_data;
                        $Cfg->{'default'}->{'access_token'}
                            = $access_token{'token'};
                        $Cfg->{'default'}->{'access_secret'}
                            = $access_token{'token_secret'};
                        $Cfg->{'default'}->{'twitter_user'}
                            = $access_token{'user'};

                        $main::TWITTER->credentials(
                            $Cfg->{'default'}->{'twitter_user'},
                            $Cfg->{'default'}->{'access_token'},
                            $Cfg->{'default'}->{'access_secret'},
                            );
                        $main::TWITTER->friends_timeline('Timeline');
                        $label_for_user->set_text($access_token{'user'})
                    },
                    );
                $assistante->start($top);
            } );

    if ( !Twitim::XMPP->is_enable ) {
        $box1->set_sensitive(FALSE);
        $box1_label->set_sensitive(FALSE);

        my $xmpp_note = Gtk2::Label->new();
        $xmpp_note->set_markup( "<small>" .l10n("XMPP Support is disabled.  It requires Net::XMPP2 and XML::Simple modules.") . "</small>");
        $xmpp_note->set_line_wrap(TRUE);
        $xmpp_note->set_alignment( 0.0, 0.0 );
        $xmpp_note->set_padding(20, 6);
        $account_box->pack_start( $xmpp_note, 0, 1, 0 );
    }

    # view and sound
    my $appearace_box = _make_vbox( $notebook, "View and Sound" );

    my $box3 = _make_frame( $appearace_box, "View",  3, 2 );
    my $box4 = _make_frame( $appearace_box, "Sound", 1, 2 );

    $self->{'entry'}->{'system_font'} = _make_check_button(
        $box3,
        "Use _System Font",
        $Cfg->{'default'}->{'system_font'}
    );
    my $font_is_set = $self->{'entry'}->{'system_font'}->get_active;
    ( $self->{'entry'}->{'font'}, my $font_label )
        = _make_font_button( $box3, "F_ont", 1, 12 );
    $self->{'entry'}->{'system_font'}->signal_connect(
        toggled => sub {
            my $widget = shift;
            _set_font_label_sensitive( $self->{'entry'}->{'font'},
                $font_label, $widget );
        }
    );
    _set_font_label_sensitive( $self->{'entry'}->{'font'},
        $font_label, FALSE )
        if $font_is_set;

    # user icon
    my $icon_size_tip = "This setting is effective for messages "
        . "which will show after closing preferences window.";
    $self->{'entry'}->{'show_user_icon'} = _make_check_button(
        $box3,
        "Show User _Icon",
        $Cfg->{'default'}->{'show_user_icon'}, 2, undef, undef, undef, 
        $icon_size_tip
    );
 
    my $scale = $self->{'entry'}->{'icon_size'}
        = Gtk2::HScale->new_with_range( 1, 3, 0.1 );
    $scale->set_value_pos('right');
    $scale->set_value($Cfg->{'default'}->{'icon_size'});

    my $icon_size_label 
        = set_label( $box3, "Icon Si_ze", 3, undef, $scale, undef, 12 );
    $box3->attach( $scale , 1, 2, 0 + 3, 1 + 3, [ 'expand', 'fill' ],
        'shrink', 0, 0 );

    $self->{'entry'}->{'show_user_icon'}->signal_connect(
        toggled => sub {
            my $widget = shift;
            _set_font_label_sensitive( $self->{'entry'}->{'icon_size'},
                $icon_size_label, $widget, TRUE );
        }
    );
    _set_font_label_sensitive( $self->{'entry'}->{'icon_size'},
        $icon_size_label, FALSE )
        if !$self->{'entry'}->{'show_user_icon'}->get_active;;

    # reverse timeline order
    $self->{'entry'}->{'reverse_timeline_order'} = _make_check_button(
        $box3,
        "_Reverse Timeline Order",
        $Cfg->{'default'}->{'reverse_timeline_order'}, 4, undef, undef, undef, 
        undef,
    );

    # delay timer
    $self->{'entry'}->{'twitter_delay'} = _make_check_button(
        $box3,
        "Enable _Delay Timer for Timeline Output",
        $Cfg->{'default'}->{'twitter_delay'}, 5, undef, undef, undef, 
        "For only messages acquired with Web API"
    );

    # sound file
    $self->{'entry'}->{'sound_file'}
        = _make_file_chooser_button( $box4, "_File", "sound_file" );

    # misc tab
    my $misc_box = _make_vbox( $notebook, "Desktop" );
    my $box6 = _make_frame( $misc_box, "Desktop", 3, 2 );

    $self->{'entry'}->{'use_geometry'} = _make_check_button(
        $box6,
        "Save _Window Size and Position",
        $Cfg->{'default'}->{'use_geometry'}
    );
    $self->{'entry'}->{'sticky'} = _make_check_button(
        $box6,
        "Always on _Visible Workspace",
        $Cfg->{'default'}->{'sticky'}, 1
    );
    $self->{'entry'}->{'skip_taskbar'} = _make_check_button(
        $box6,
        "Skip _Taskbar",
        $Cfg->{'default'}->{'skip_taskbar'}, 2
    );
    $self->{'entry'}->{'icon'}
        = _make_file_chooser_button( $box6, "_Icon", "icon", 3,
        "This setting will be effective when Twitim is restarted." )
        unless ( -e '/etc/twitim/twitim.png' );

    my $box7 = _make_frame( $misc_box, "Misc", 2, 2 );

    if ($main::AUTO_START->is_ok) {
        my $auto_start = _make_check_button( $box7, "_Auto Start",
            $main::AUTO_START->get );
        $auto_start->signal_connect(
            toggled => sub {
                my $widget = shift;
                $main::AUTO_START->set( $widget->get_active );
            }
        );
    }
    $self->{'entry'}->{'send_with_ctrl'} = _make_check_button(
        $box7,
        "Send Messages with Ctrl + Enter _Keys",
        $Cfg->{'default'}->{'send_with_ctrl'}, 1
    );
    if ( $Mainwin->check_spell_is_ok ) {
        $self->{'entry'}->{'spell_check'} = _make_check_button(
            $box7,
            "Enable _Spell Checking",
            $Cfg->{'default'}->{'spell_check'}, 2
        );
    }

    # custom tab
    my $tab_box = _make_vbox( $notebook, "Custom Tabs" );
    my $box5 = _make_frame( $tab_box, "Custom Tabs", 'h' );
    ( my $hbox, my $slist ) = _make_slist($box5);
    $self->{'slist'} = $slist;

    my @s_button;
    $s_button[0] = _make_button( $hbox, 'gtk-new',
        sub { $self->_add_new_item($slist) } );
    $s_button[1] = _make_button( $hbox, 'gtk-preferences',
        sub { $self->_edit_item_properties($slist) } );
    $s_button[2] = _make_button( $hbox, 'gtk-delete',
        sub { _delete_item( $slist, \@s_button ) } );
    $s_button[3] = _make_button( $hbox, 'gtk-go-up',
        sub { _move_item( $slist, -1, \@s_button ) } );
    $s_button[4] = _make_button( $hbox, 'gtk-go-down',
        sub { _move_item( $slist, 1, \@s_button ) } );

    _set_tab_prop_buttons_sensitive(@s_button);

    # signal
    $slist->signal_connect(
        cursor_changed => sub { _select_slist( $slist, \@s_button ) } );

    # bottom hbox buttons
    my $button_box = Gtk2::HButtonBox->new();
    $mainbox->pack_start( $button_box, 0, 0, 0 );

    _make_button( $button_box, 'gtk-help' );
    _make_button( $button_box, 'gtk-close', $self->{'on_delete'} );

    $top->signal_connect( destroy      => $self->{'on_destroy'} );
    $top->signal_connect( delete_event => $self->{'on_delete'} );

    $top->show_all();

    # set notebook page
    my $page_num = $Cfg->{'default'}->{'recent_preferences'} || 0;
    $notebook->set_current_page($page_num);
}

sub save {
    my $self = shift;

    _get_properties( 'default', $self->{'entry'} );

    # save watchlist new order, sort keys
    my @watchlist = ();

    for ( my $i = 0; $i <= $#{ $self->{'slist'}->{'data'} }; $i++ ) {
        my $watchlist_name   = $self->{'slist'}->{'data'}[$i][1];
        my $watchlist_in_use = $self->{'slist'}->{'data'}[$i][0];

        $watchlist_name = _l10n_watchlist( $watchlist_name, TRUE );
        $Cfg->{$watchlist_name}->{'sort_key'} = $i;
        $Cfg->{$watchlist_name}->{'in_use'}   = $watchlist_in_use;
        push @watchlist, $watchlist_name if $watchlist_in_use;
    }

    return @watchlist;
}

sub quit {
    my $self = shift;

    # save notebook page
    $Cfg->{'default'}->{'recent_preferences'}
        = $self->{'preferences_notebook'}->get_current_page;

    delete $self->{'preferences_win'};
    delete $self->{'tab_properties'} if exists $self->{'tab_properties'};
}

sub present {
    my $self = shift;
    $self->{'preferences_win'}->present;
}

sub destroy {
    my $self = shift;
    $self->{'preferences_win'}->destroy;
}

# preferences signal process

sub _add_new_item {
    my ( $self, $slist ) = @_;
    my $index = $#{ $slist->{'data'} } + 1;
    $self->_start_tab_props( $index, $slist );
}

sub _edit_item_properties {
    my ( $self, $slist ) = @_;
    my @indices = $slist->get_selected_indices;
    my $index = $indices[0] || 0;
    $self->_start_tab_props( $indices[0], $slist );
}

sub _delete_item {
    my ( $slist, $s_button ) = @_;
    my @indices   = $slist->get_selected_indices;
    my $watchlist = $slist->{'data'}[ $indices[0] ][1];
    $watchlist = _l10n_watchlist( $watchlist, TRUE );

    delete $slist->{'data'}[ $indices[0] ];
    delete $Cfg->{$watchlist};
    _set_tab_prop_buttons_sensitive(@$s_button);
}

sub _move_item {
    my ( $slist, $n, $s_button ) = @_;
    my @indices = $slist->get_selected_indices;
    my $i       = $indices[0];

    foreach my $column ( 0, 1 ) {
        my $from = \$slist->{'data'}[$i][$column];
        my $to   = \$slist->{'data'}[ $i + $n ][$column];
        ( $$from, $$to ) = ( $$to, $$from );
    }

    $slist->select( $i + $n );
    _select_slist( $slist, $s_button );
}

sub _select_slist {
    my ( $slist, $s_button ) = @_;

    my @indices = $slist->get_selected_indices;
    my $row     = $indices[0];

    return unless ( defined $row );

    for ( my $i = 1; $i <= $#{$s_button}; $i++ ) {
        $s_button->[$i]->set_sensitive(TRUE);
    }

    $s_button->[3]->set_sensitive(FALSE) if $row == 0;
    $s_button->[4]->set_sensitive(FALSE) if $row == $#{ $slist->{'data'} };
}

# preferences utils

sub _make_vbox {
    my ( $parent, $name ) = @_;

    my $vbox = Gtk2::VBox->new( 0, 10 );
    $parent->append_page( $vbox, l10n($name) );
    $vbox->set_border_width(10);

    return $vbox;
}

sub _make_frame {
    my ( $parent, $name, $row, $col, $return_label ) = @_;
    my ( $contents, $up_padding );
    my $expand = 1;
    $row ||= "";

    if ( defined $col ) {
        $contents = Gtk2::Table->new( $row, $col );
        $contents->set_col_spacings(12);
        $contents->set_row_spacings(6);
        $contents->set_border_width(6);
        $up_padding = 0;
        $expand     = 0;
    }
    elsif ( $row eq 'h' ) {
        $contents = Gtk2::HBox->new( 0, 10 );
        $up_padding = 12;

        # $contents->set_border_width(0);
    }
    else {
        $contents = Gtk2::VBox->new();
        $contents->set_border_width(6);
        $up_padding = 6;
    }

    my $alignment = Gtk2::Alignment->new( 0.5, 0.5, 1.0, 1.0 );
    $alignment->set_padding( $up_padding, 0, 12, 0 );
    $alignment->add($contents);

    my $framelabel = Gtk2::Label->new();
    $framelabel->set_markup( sprintf( "<b>%s</b>", l10n($name) ) );

    my $frame = Gtk2::Frame->new();
    $frame->set_shadow_type('none');
    $frame->set_label_widget($framelabel);
    $frame->add($alignment);

    $parent->pack_start( $frame, $expand, 1, 0 );

    return ( $contents, $framelabel ) if $return_label;
    return $contents;
}

sub _make_file_chooser_button {
    my ( $parent, $name, $conf, $n, $tip ) = @_;
    $n ||= 0;

    my $title = "$name Selection";
    $title =~ tr/_//d;

    my $widget = Gtk2::FileChooserButton->new( l10n($title), 'open' );
    my $filename = $Cfg->{'default'}->{$conf} || "";
    $widget->set_filename($filename) if -r $filename;
    $widget->set_tooltip_markup( l10n($tip) ) if defined $tip;

    set_label( $parent, $name, $n, undef, $widget );
    $parent->attach( $widget, 1, 2, 0 + $n, 1 + $n, [ 'expand', 'fill' ],
        'shrink', 0, 0 );

    $widget->show;
    return $widget;
}

sub _make_font_button {
    my ( $parent, $name, $n, $padding ) = @_;
    $n ||= 0;
    $padding ||= 0;

    my $widget = Gtk2::FontButton->new();
    $widget->set_use_font(TRUE);

    my $font_name = $Cfg->{'default'}->{'font'};
    if ( defined $font_name ) {
        $widget->set_font_name($font_name);
    }

    my $label = 
        set_label( $parent, $name, $n, undef, $widget, undef, $padding );
    $parent->attach( $widget, 1, 2, 0 + $n, 1 + $n, [ 'expand', 'fill' ],
        'shrink', 0, 0 );
    return ( $widget, $label );
}

=pod
sub _make_combobox {
    my ( $parent, $name, $config, $row, $list, $tip, $return_label_obg ) = @_;

    my $index = $config || 0;

    my $combo = Gtk2::ComboBox->new_text;
    $combo->set_tooltip_markup( l10n($tip) ) if ( defined $tip );

    foreach my $text (@$list) {
        $combo->append_text( l10n($text) );
    }

    $combo->set_active($index);
    my $label_obj = set_label( $parent, $name, $row, undef, $combo );
    $parent->attach( $combo, 1, 2, 0 + $row, 1 + $row, [ 'expand', 'fill' ],
        'shrink', 0, 0 );

    return ( $combo, $label_obj ) if ($return_label_obg);
    return $combo;
}
=cut

sub _set_font_label_sensitive {
    my ( $font_box, $label, $state, $reverse ) = @_;

    if ( $state =~ /CheckButton/ ) {
        $state = $state->get_active;
        $state = not $state if !$reverse;
    }

    $font_box->set_sensitive($state);
    $label->set_sensitive($state);
}

sub _make_slist {
    my ( $parent, $name, $n ) = @_;
    my @slist_array;

    my $scrolled_window = Gtk2::ScrolledWindow->new();
    $scrolled_window->set_policy( 'automatic', 'automatic' );
    $scrolled_window->set_shadow_type('etched-in');
    $scrolled_window->add(
        my $slist = Gtk2::SimpleList->new(
            'Use'      => 'bool',
            'Tab Name' => 'text'
        )
    );

    my $tip = "Each check box is only for default opend tabs.  The notification setting does not depend on the check box";
    $scrolled_window->set_tooltip_markup( l10n($tip) );

    my @all_watchlist = Twitim::MainWindow::get_watchlist( $Cfg, TRUE );

    for ( my $i = 0; $i <= $#all_watchlist; $i++ ) {
        my $wl_in_use = ( exists $Cfg->{"$all_watchlist[$i]"}->{'in_use'}
                && $Cfg->{"$all_watchlist[$i]"}->{'in_use'} == 0 ) ? 0 : 1;
        my $wl_name = _l10n_watchlist( $all_watchlist[$i] );
        $slist_array[$i] = [ $wl_in_use, $wl_name ];
    }

    $slist->set_data_array( \@slist_array );
    $slist->set_headers_visible(FALSE);
    my $vbutton_box = Gtk2::VButtonBox->new;
    $vbutton_box->set_spacing(8);
    $vbutton_box->set_layout_default('start');

    $parent->pack_start( $scrolled_window, 1, 1, 0 );
    $parent->pack_start( $vbutton_box,     0, 0, 0 );

    return ( $vbutton_box, $slist );
}

sub _set_tab_prop_buttons_sensitive {
    my @s_button = @_;
    for ( my $i = 1; $i <= $#s_button; $i++ ) {
        $s_button[$i]->set_sensitive(FALSE);
    }
}

###########################################################################
# custom tab properties

sub _start_tab_props {
    my ( $self, $watchlist_index, $slist ) = @_;    # combobox item num, slist
    my ( %entry, %label );
    $watchlist_index ||= 0;
    my $watchlist
        = ( $watchlist_index <= $#{ $slist->{'data'} } )
        ? $slist->{'data'}[$watchlist_index][1]
        : "";
    $watchlist = _l10n_watchlist( $watchlist, TRUE );
    my @tab_types = _get_tab_types();

    if ( $self->{'tab_properties'} ) {
        $self->{'tab_properties'}->present;
        return;
    }

    my $top = $self->{'tab_properties'} = _make_prefs_top_win(
        'Tab Properties',
        $self->{'preferences_win'},
        350, 400
    );
    $top->set_destroy_with_parent(TRUE);
    $top->signal_connect( delete_event => \&_close_tab_props, $self );
    $top->signal_connect( destroy => $self->{'on_destroy_tab_prop'} );
    $top->add( my $mainbox = Gtk2::VBox->new( 0, 12 ) );

    my $notebook = Gtk2::Notebook->new();
    $mainbox->pack_start( $notebook, 1, 1, 0 );

    ######################################################################
    # General Box
    my $general_box = _make_vbox( $notebook, "General" );

    my $box1 = _make_frame( $general_box, "Tab", 2, 2 );
    ( my $box2, $label{'label_buddy'} )
        = _make_frame( $general_box, "Pick up Users", undef, undef, TRUE );
    ( my $box3, $label{'label_keyword'} )
        = _make_frame( $general_box, "Pick up Keywords", undef, undef, TRUE );

    # tab name
    my $combo = _make_tab_combobox( $box1, $watchlist, $slist );
    set_label( $box1, "_Tab Type", undef, undef, $combo );

    my $custom_name = ( !grep /^$watchlist$/, @tab_types ) ? $watchlist : "";
    ( $label{'custom_tab_name'}, $label{'label_name'} )
        = text_entry( $box1, "Custom _Name", $custom_name, 1, FALSE, TRUE );

    # config
    my ( %text, %notification );
    if ( defined $Cfg->{$watchlist} ) {    # avoid vacant tab
        $text{'buddy'}   = $Cfg->{$watchlist}->{'buddy'};
        $text{'keyword'} = $Cfg->{$watchlist}->{'keyword'};

        $notification{'expire_type'} =
            $Cfg->{$watchlist}->{'notification_expire_type'};
        $notification{'expire_time'} =
            $Cfg->{$watchlist}->{'notification_expire_time'};
        $notification{'expire_in_iding'} =
            $Cfg->{$watchlist}->{'notification_expire_in_iding'};

        if ( $Cfg->{$watchlist}->{'notification'} ) {
            # for backward compatibilities
            if ( $Cfg->{$watchlist}->{'notification'} > 1 ) {
                $notification{'popup'} = 1;
            }
            if ( $Cfg->{$watchlist}->{'notification'} % 2 ) {
                $notification{'sound'} = 1;
            }
            delete $Cfg->{$watchlist}->{'notification'};
        } 
        else {
            $notification{'sound'}
                = $Cfg->{$watchlist}->{'notification_sound'};
            $notification{'popup'}
                = $Cfg->{$watchlist}->{'notification_popup'};
        }
    }

    # users & keywords
    $entry{'buddy'} = _set_tab_props_textview( $box2, $text{'buddy'},
        l10n("Enter comma separated list of users") );
    $entry{'keyword'} = _set_tab_props_textview( $box3, $text{'keyword'},
        l10n("Enter comma separated list of keywords") );

    ######################################################################
    # Notification Box

    my $notification_box = _make_vbox( $notebook, "Notification" );

    # Sounds
    ( my $sound_box, $label{'label_sound'} )
        = _make_frame( $notification_box, "Sound", 2, 2, TRUE );

    if ( !$main::NOTIFY->sound_is_ok ) {
        $label{'label_sound'}->set_sensitive(FALSE);
        $sound_box->set_sensitive(FALSE);
    }

    $entry{'notification_sound'} = _make_check_button(
        $sound_box, "Enable _sounds", $notification{'sound'} );

    # Pop-ups
    ( my $popups_box, $label{'label_popup'} )
        = _make_frame( $notification_box, "Pop-up", 2, 4, TRUE );

    if ( !$main::NOTIFY->dbus_is_ok ) {
        $label{'label_popup'}->set_sensitive(FALSE);
        $popups_box->set_sensitive(FALSE);
    }

    $entry{'notification_popup'}
        = _make_check_button( $popups_box, "Enable _pop-ups",
        $notification{'popup'}, undef, undef, undef, 2 );

    # Pop-ups Expiration
    $label{'label_expire'}
        = Gtk2::Label->new_with_mnemonic( l10n('_Expiration') . ':' );
    $popups_box->attach( $label{'label_expire'}, 0, 1, 1, 2, 'fill', 'shrink',
        0, 0 );

    $entry{'notification_expire_type'} = Gtk2::ComboBox->new_text;
    foreach my $text ( "Default", "Never", "After" ) {
        $entry{'notification_expire_type'}->append_text( l10n($text) );
    }
    $notification{'expire_type'} ||= 0;
    $entry{'notification_expire_type'}
        ->set_active( $notification{'expire_type'} );
    $popups_box->attach(
        $entry{'notification_expire_type'},
        1, 2, 1, 2, [ 'expand', 'fill' ],
        'shrink', 0, 0
    );
    $label{'label_expire'}
        ->set_mnemonic_widget( $entry{'notification_expire_type'} );

    $entry{'notification_expire_time'}
        = Gtk2::SpinButton->new_with_range( 0, 9999, 1 );
    $notification{'expire_time'} ||= 0;
    $entry{'notification_expire_time'}
        ->set_value( $notification{'expire_time'} );
    $popups_box->attach_defaults( $entry{'notification_expire_time'},
        2, 3, 1, 2 );

    $label{'label_expire_sec'} = Gtk2::Label->new( l10n('sec') );
    $popups_box->attach( $label{'label_expire_sec'},
        3, 4, 1, 2, 'fill', 'shrink', 0, 0 );

    $entry{'notification_expire_in_iding'}
        = _make_check_button( $popups_box, "Disable expiration when _idling",
        $notification{'expire_in_iding'}, 3, 12, 0, 4);

    ######################################################################
    # Buttons

    my $button_box = Gtk2::HButtonBox->new();
    $mainbox->pack_start( $button_box, 0, 0, 0 );

    my $save_prefs = sub {
        $self->_save_tab_props( $watchlist_index, $watchlist, $top, $slist,
            $combo, \%entry, \%label );
    };

    # _make_button($button_box, 'gtk-help');
    _make_button( $button_box, 'gtk-cancel',
        sub { $self->_close_tab_props; } );
    _make_button( $button_box, 'gtk-ok', $save_prefs );

    # tab properties sensitive
    if ( $#tab_types != $combo->get_active ) {
        my %widget = ( %entry, %label );
        _set_tab_props_sensitive( \%widget, $combo, FALSE );
    }

    # expire type sensitive
    my %target = ( %entry, %label );

    my $notification_is_on = $entry{'notification_popup'}->get_active;
    $label{'label_expire'}->set_sensitive($notification_is_on);
    $entry{'notification_expire_type'}->set_sensitive($notification_is_on);

    my $expire_state = $notification_is_on ? undef : FALSE;
    _set_sensitive_expire_sec( $entry{'notification_expire_type'},
        \%target, $expire_state );

    # signals
    $combo->signal_connect(
        changed => sub {
            my $state = ( $#tab_types == $combo->get_active ) ? TRUE : FALSE;
            my %widget = ( %entry, %label );
            _set_tab_props_sensitive( \%widget, $combo, $state );
        }
    );

    $entry{'notification_popup'}->signal_connect(
        toggled => sub {
            my $widget = shift;
            my $state  = $widget->get_active;

            $label{'label_expire'}->set_sensitive($state);
            $entry{'notification_expire_type'}->set_sensitive($state);
            _set_sensitive_expire_sec( $widget, \%target, FALSE );

            if ($state) {
                _set_sensitive_expire_sec( $entry{'notification_expire_type'},
                    \%target );
            }
        }
    );

    $entry{'notification_expire_type'}->signal_connect(
        changed => sub {
            my $widget = shift;
            _set_sensitive_expire_sec( $widget, \%target );
        }
    );

    $top->show_all;
}

# tab properties signal process

sub _set_sensitive_expire_sec {
    my ( $widget, $target, $state ) = @_;

    my $time_state   = ( $widget->get_active == 2 ) ? TRUE  : FALSE;
    my $idling_state = ( $widget->get_active == 1 ) ? FALSE : TRUE;

    if (defined $state) {
        $time_state = $idling_state = $state;
    }

    $target->{'notification_expire_time'}->set_sensitive($time_state);
    $target->{'label_expire_sec'}->set_sensitive($time_state);
    $target->{'notification_expire_in_iding'}->set_sensitive($idling_state);
}

sub _close_tab_props {
    my $self = shift;

    $self->{'tab_properties'}->destroy;
    delete $self->{'tab_properties'};
}

sub _save_tab_props {
    my ( $self, $watchlist_index, $watchlist, $window, $slist, $combo, $entry,
        $label )
        = @_;

    my @tab_types   = _get_tab_types();
    my $index       = $combo->get_active();
    my $custom_name = $label->{'custom_tab_name'}->get_text;
    my $block = ( $index < $#tab_types ) ? $tab_types[$index] : $custom_name;

    my @twitter_tablist = $main::TWITTER->get_api_names;
    if ( grep /^$custom_name$/, @twitter_tablist ) {
        _show_custom_tab_error_dialog($window);
        return;
    }

    my @slist_tab_name = _get_slist_tab_names($slist);

    unless ($block) {
        my $num = 0;
        do {
            $block = l10n("Untitled") . " $num";
            $num++;
        } while ( grep /^$block$/, @slist_tab_name );
    }

    my %entry_with_combo = %$entry;
    $entry_with_combo{'custom_tab_type'} = $combo;

    if ( $block ne $watchlist ) {
        delete $Cfg->{$watchlist};    # delete old one
            #  $main::WATCHLIST[$watchlist_index] = $block; # FIXME
    }

    my $new_block = _get_properties( $block, $entry );

    # delete unnecessary data
    if ( $index < $#tab_types ) {
        delete $Cfg->{$block}->{'keyword'};
        delete $Cfg->{$block}->{'buddy'};

        $Cfg->{$block}->{'notification'} = 0
            unless ( $block eq 'Replies' || $block eq 'Direct Messages' );
    }

    # update SimpleList
    if ( $watchlist_index <= $#{ $slist->{'data'} } ) {
        $slist->{'data'}[$watchlist_index][1] = _l10n_watchlist($new_block);
    }
    else {
        my $tmp = _l10n_watchlist($new_block);
        push @{ $slist->{'data'} }, [ TRUE, $tmp ]
            unless ( grep /^$tmp/, @slist_tab_name );
    }

    # $slist->select($watchlist_index);
    $self->_close_tab_props;
}

# tab properties utils

sub _get_slist_tab_names {
    my $slist = shift;
    my @slist_tab_name;

    for ( my $i = 0; $i <= $#{ $slist->{'data'} }; $i++ ) {
        push @slist_tab_name, $slist->{'data'}[$i][1];
    }

    return @slist_tab_name;
}

sub _show_custom_tab_error_dialog {
    my $parent = shift;

    my $text1 = "This custom name is unavailable.";
    my $text2
        = "This name is reserved for tab types.  Please change the name.";

    my $dialog = make_dialog( $text1, $text2, $parent, 'warning', 'ok' );
}

sub _make_tab_combobox {
    my ( $parent, $watchlist, $slist ) = @_;
    my $combo          = Gtk2::ComboBox->new_text;
    my @slist_tab_name = _get_slist_tab_names($slist);
    my @tab_types      = _get_tab_types();
    my $index          = $#tab_types;

    for ( my $i = 0; $i <= $#tab_types; $i++ ) {
        my $default_tab_name = l10n( $tab_types[$i] );
        $combo->append_text($default_tab_name);
        $index = $i if $tab_types[$i] eq $watchlist;
    }

    $combo->set_active($index);
    $parent->attach( $combo, 1, 2, 0, 1, [ 'expand', 'fill' ],
        'shrink', 0, 0 );

    return $combo;
}

sub _set_tab_props_sensitive {
    my ( $widget, $combo, $state ) = @_;
    my $combo_name = $combo->get_active_text;

    foreach my $key ( keys %{$widget} ) {
        $widget->{$key}->set_sensitive($state);
    }

    if (   $combo_name eq _l10n_watchlist("Replies")
        || $combo_name eq _l10n_watchlist("Direct Messages") )
    {
        $widget->{'label_sound'}->set_sensitive(TRUE);
        $widget->{'label_popup'}->set_sensitive(TRUE);
        $widget->{'notification_sound'}->set_sensitive(TRUE);
        $widget->{'notification_popup'}->set_sensitive(TRUE);

        $widget->{'notification_expire_type'}->set_sensitive(TRUE);
        $widget->{'label_expire'}->set_sensitive(TRUE);
    }

    if (   $combo_name eq _l10n_watchlist("Replies")
        || $combo_name eq _l10n_watchlist("Direct Messages")
        || $state == TRUE )
    {
        _set_sensitive_expire_sec( $widget->{'notification_expire_type'},
            $widget );
    }
}

sub _set_tab_props_textview {
    my ( $parent, $text, $tooltip ) = @_;

    my $scrolled_window = Gtk2::ScrolledWindow->new();
    $scrolled_window->set_policy( 'automatic', 'automatic' );
    $scrolled_window->set_shadow_type('etched-in');
    $scrolled_window->set_tooltip_markup($tooltip);
    $scrolled_window->add( my $text_view = Gtk2::TextView->new() );

    $text_view->set_wrap_mode('word-char');
    $text_view->set_left_margin(3);    # FIXME
    $text_view->set_right_margin(3);
    $text_view->set_pixels_above_lines(1);
    $text_view->set_pixels_below_lines(5);
    $text_view->set_pixels_inside_wrap(2);

    $text_view->get_buffer->set_text($text) if $text;
    $parent->pack_start( $scrolled_window, 1, 1, 0 );

    return $text_view;
}

###########################################################################
# common

sub _make_prefs_top_win {
    my ( $name, $parent, $w, $h ) = @_;

    my $top = Gtk2::Window->new();

    #$top->set_modal(TRUE);
    #$top->set_resizable(FALSE);
    $top->set_icon_from_file($main::APP_ICON) if $main::APP_ICON;
    $top->set_transient_for($parent);
    $top->set_default_size( $w, $h );
    $top->set_title( l10n($name) );
    $top->set_border_width(12);
    $top->set_skip_taskbar_hint(TRUE);
    $top->set_skip_pager_hint(TRUE);
    $top->set_type_hint('dialog');

    return $top;
}

sub _make_button {
    my ( $parent, $stock, $func ) = @_;

    my $button = Gtk2::Button->new_from_stock($stock);
    $button->signal_connect( clicked => $func );
    $parent->pack_end( $button, 0, 0, 0 );

    return $button;
}

sub _make_auth_button {
    my ( $parent, $stock, $func ) = @_;

    my $text = l10n("_Authorization");
    my $button = Gtk2::Button->new_with_mnemonic($text);
    $button->signal_connect( clicked => $func );
    $parent->attach_defaults( $button, 0, 2, 1, 2 );

    return $button;
}

sub _make_check_button {
    my ( $parent, $text, $conf, $n, $padding, $left, $right, $tip ) = @_;
    $n ||= 0;
    $padding ||= 0;
    $left ||= 0;
    $right ||= 2;

    my $widget = Gtk2::CheckButton->new_with_mnemonic( l10n($text) );
    $widget->set_tooltip_markup( l10n($tip) ) if defined $tip;

    my $state = $conf || FALSE;
    $widget->set_active($state);
    $parent->attach( $widget, $left, $right, 0 + $n, 1 + $n,
        [ 'expand', 'fill' ],
        'shrink', $padding, 0 );

    return $widget;
}

sub _get_properties {
    my ( $block, $entry ) = @_;
    my ( $update_xmpp, $update_api );

    foreach my $key ( keys %$entry ) {
        my $data;

        if ( $entry->{$key}->isa('Gtk2::Entry') ) {
            $data = $entry->{$key}->get_text;
        }
        elsif ( $entry->{$key}->isa('Gtk2::CheckButton') ) {
            $data = $entry->{$key}->get_active;
        }
        elsif ( $entry->{$key}->isa('Gtk2::FileChooserButton') ) {
            $data = $entry->{$key}->get_filename;
        }
        elsif ( $entry->{$key}->isa('Gtk2::FontButton') ) {
            $data = $entry->{$key}->get_font_name;
        }
        elsif ( $entry->{$key}->isa('Gtk2::ComboBox') ) {
            $data = $entry->{$key}->get_active;
        }
        elsif ( $entry->{$key}->isa('Gtk2::HScale') ) {
            $data = $entry->{$key}->get_value;
        }
        elsif ( $entry->{$key}->isa('Gtk2::TextView') ) {
            my $text_buffer = $entry->{$key}->get_buffer;
            my ( $start, $end ) = $text_buffer->get_bounds;
            $data = $text_buffer->get_text( $start, $end, 1 );
            $data =~ s/\s+/ /g;
            $data =~ s/\s*,\s*/, /g;
        }

        if ( $block eq 'default' ) {
            my ( $xmpp, $api ) = _update_settings( $key, $data, $entry );
            $update_xmpp = 1 if $xmpp;
            $update_api  = 1 if $api;
        }

        # save data
        if ($data) {
            chomp($data);
            $Cfg->{$block}->{$key} = $data;

            # print "$key: ", $data, "\n";
        }
        else {
            delete $Cfg->{$block}->{$key};

            if (   $key eq 'system_font'
                || $key eq 'use_geometry'
                || $key eq 'show_user_icon' )
            {
                $Cfg->{$block}->{$key} = 0;
            }
        }
    }

    if ($update_xmpp) {
        $main::XMPP->credentials(
            $Cfg->{'default'}->{'xmpp_user'},
            $Cfg->{'default'}->{'xmpp_pass'}
        );
        $main::XMPP->connect;
    }

    return $block;
}

sub _update_settings {
    my ( $key, $new, $entry ) = @_;
    my ( $update_xmpp, $update_api );

    # change font
    if ( $key eq 'font' ) {
        $Mainwin->change_font($new)
            if ( $Cfg->{'default'}{'font'} ne $new );
    }
    elsif ( $key eq 'system_font' ) {
        my $old = $Cfg->{'default'}{'system_font'};

        $Mainwin->change_font()
            if ( $old == 0 && $new == 1 );
        $Mainwin->change_font( $entry->{'font'}->get_font_name )
            if ( $old == 1 && $new == 0 );
    }

    # change timeline order
    elsif ( $key eq 'reverse_timeline_order' ) {
        my $old = $Cfg->{'default'}{'reverse_timeline_order'} || 0;
                $Mainwin->reset_text_view() if $old != $new;
    }

    # change delay
    elsif ( $key eq 'twitter_delay' ) {
        my $old = $Cfg->{'default'}{'twitter_delay'} || 0;
        $main::TWITTER->set_delay($new) if $old != $new;
    }

    # change sticky
    elsif ( $key eq 'sticky' ) {
        my $old = $Cfg->{'default'}{'sticky'} || 0;

        if ( $old == 0 && $new == 1 ) {
            $Mainwin->{'top'}->stick;
        }
        elsif ( $old == 1 && $new == 0 ) {
            $Mainwin->{'top'}->unstick;
        }
    }

    # change skip taskbar
    elsif ( $key eq 'skip_taskbar' ) {
        my $old = $Cfg->{'default'}{'skip_taskbar'} || 0;
        $Mainwin->{'top'}->set_skip_taskbar_hint($new) if $old != $new;
    }

    # change spell check
    elsif ( $key eq 'spell_check' ) {
        my $old = $Cfg->{'default'}{'spell_check'} || 0;
        $Mainwin->enable_check_spell($new) if $old != $new;
    }

    # change XMPP account
    elsif ( $key eq 'xmpp_user' || $key eq 'xmpp_pass' ) {
        my $old = $Cfg->{'default'}{$key} || 0;
        my $new_tmp = $new || 0;

        if ($old ne $new_tmp) {
            $Mainwin->set_menu_sensitive( "Connect", FALSE );
            $Mainwin->set_menu_sensitive( "Twitter", FALSE );
            $update_xmpp = 1;
        }
    }

    return ( $update_xmpp, $update_api );
}

sub _l10n_watchlist {
    my ( $text, $to_eng ) = @_;
    my ( %eng2locale, %locale2eng );

    my @list = grep( !/^Custom$/, _get_tab_types() );
    push @list, 'Timeline';

    foreach my $key (@list) {
        $eng2locale{$key} = l10n($key);
        $locale2eng{ l10n($key) } = $key;
    }

    return $locale2eng{$text} if $to_eng && $locale2eng{$text};
    return $eng2locale{$text} if $eng2locale{$text};
    return $text;
}

sub _get_tab_types {
    my @type;

    foreach my $key ( Twitim::Twitter->get_api_array ) {
        push @type, $key->[0] unless ( $key->[0] eq 'Timeline' );
    }

    push @type, 'Custom';
    return @type;
}

1;
